<?php


namespace App\Utils;


class RSA
{
    /**
     * private key
     */
    private $_privKey;

    /**
     * public key
     */
    private $_pubKey;

    private $size_enc = (1024 / 8) - 11;    // 这里主要是符合sip的加密格式，他们指定的长度是这样
    private $size_dec = (1024 / 8);         // 2014是rsa秘钥长度


    public function __construct ($priKeyFile = null, $pubKeyFile = null)
    {
        //设置私钥
        if ($priKeyFile) {
            $this->setPrivKey($priKeyFile);
        }

        // 设置公钥
        if ($pubKeyFile) {
            $this->setPubKey($pubKeyFile);
        }
        if (!defined('RSA_ENC_PRI')) {
            define('RSA_ENC_PRI', 0);
            define('RSA_ENC_PUB', 1);
            define('RSA_DEC_PRI', 2);
            define('RSA_DEC_PUB', 3);
        }

    }



    /**
     * setup the private key
     */
    public function setPrivKey ($priKeyFile) : RSA
    {
        $prk = file_get_contents($priKeyFile);
        $this->_privKey = openssl_pkey_get_private($prk);
        return $this;
    }

    /**
     * setup the public key
     */
    public function setPubKey ($pubKeyFile) : RSA
    {
        $puk = file_get_contents($pubKeyFile);
        $this->_pubKey = openssl_pkey_get_public($puk);
        return $this;
    }

    /**
     * @param $data
     * @param $type
     * @return string
     */
    private function encrypt($data, $type) : string {
        if (!is_string($data)) {
            return '';
        }
        $dataArr = str_split($data, $this->size_enc);
        $res = "";
        foreach ($dataArr as $data) {
            switch ($type) {
                case RSA_ENC_PRI: $r = openssl_private_encrypt($data, $encrypted, $this->_privKey); break;
                case RSA_ENC_PUB: $r = openssl_public_encrypt($data, $encrypted, $this->_pubKey); break;
                default: $r = null;
            }
            if (!$r) {
                return '';
            }

            $res .= $encrypted;
        }
        return base64_encode($res);
    }

    /**
     * @param $crypted
     * @param $type
     * @return string
     */
    private function decrypt($crypted, $type) : string {

        if (!is_string($crypted)) {
            return '';
        }

        $crypted = base64_decode($crypted);
        $dataArr = str_split($crypted, $this->size_dec);
        $res = '';
        foreach ($dataArr as $data) {
            switch ($type) {
                case RSA_DEC_PUB: $r = openssl_public_decrypt($data, $decrypted, $this->_pubKey); break;
                case RSA_DEC_PRI: $r = openssl_private_decrypt($data, $decrypted, $this->_privKey);break;
                default: $r = null;
            }

            if (!$r) {
                return '';
            }
            $res.=$decrypted;
        }
        return $res;
    }

    /**
     * @function 私钥加密
     * @param $data
     * @return string|null
     */
    public function privEncrypt ($data)
    {
        return $this->encrypt($data, RSA_ENC_PRI);
    }

    /**
     * @function 私钥解密
     * @param $data
     * @return string|null
     */
    public function privDecrypt ($encrypted)
    {
       return $this->decrypt($encrypted, RSA_DEC_PRI);
    }

    /**
     * @function 公钥加密
     * @param $data
     * @return string|null
     */
    public function pubEncrypt ($data)
    {
        return $this->encrypt($data, RSA_ENC_PUB);
    }

    /**
     * @function 公钥解密
     * @param $data
     * @return string|null
     */
    public function pubDecrypt ($crypted)
    {
        return $this->decrypt($crypted, RSA_DEC_PUB);
    }

    /**
     * @function 私钥加签
     * @param $data
     * @return string|null
     */
    public function priSign ($data)
    {
        if (!is_string($data)) {
            return null;
        }
        openssl_sign($data, $sign, $this->_privKey, OPENSSL_ALGO_MD5);
        //base64编码
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * @function 公钥验签
     * @param $data
     * @param $sign
     * @return string|null
     */
    public function pubVerify($data, $sign){
        if (!is_string($data)) {
            return null;
        }
        return (bool)openssl_verify($data, base64_decode($sign), $this->_pubKey, OPENSSL_ALGO_MD5);
    }

    public function __destruct ()
    {
        empty($this->_privKey) ? '' : openssl_free_key($this->_privKey);
        empty($this->_pubKey) ? '' : openssl_free_key($this->_pubKey);
    }

}
